package main

import (
	"bytes"
	"github.com/gin-gonic/gin"
	"github.com/sergi/go-diff/diffmatchpatch"
	"html"
	"strconv"
	"strings"
)

const (
	ERROR   = -1
	SUCCESS = 0
)

func main() {
	router := gin.Default()
	gin.DisableConsoleColor()
	gin.SetMode(gin.ReleaseMode)
	router.Static("/html", "./html")

	router.POST("/", func(c *gin.Context) {
		content := c.DefaultPostForm("content", "")
		newContent := c.DefaultPostForm("new_content", "")
		if content == "" || newContent == "" {
			c.JSON(200, gin.H{
				"code":    ERROR,
				"message": "content or new_content can not be empty",
				"data":    nil,
			})
		} else {
			res := Diff(content, newContent)
			c.JSON(200, gin.H{
				"code":    SUCCESS,
				"message": "success",
				"data":    res,
			})
		}
	})

	router.POST("/html", func(c *gin.Context) {
		content := c.DefaultPostForm("content", "")
		newContent := c.DefaultPostForm("new_content", "")
		isDelStr := c.DefaultPostForm("is_del", "1")
		isDel, _ := strconv.Atoi(isDelStr)

		if content == "" || newContent == "" {
			c.JSON(200, gin.H{
				"code":    ERROR,
				"message": "content or new_content can not be empty",
				"data":    nil,
			})
		} else {
			res := DiffHtml(content, newContent, isDel == 1)
			c.JSON(200, gin.H{
				"code":    SUCCESS,
				"message": "success",
				"data":    res,
			})
		}
	})

	router.Run(":80")
}

type DiffRune struct {
	Type   diffmatchpatch.Operation
	Word   string
	Offset int32
}

func Diff(content string, newContent string) []*DiffRune {
	dmp := diffmatchpatch.New()
	diffs := dmp.DiffMain(content, newContent, false)
	return DiffPrettyRune(diffs)
}

func DiffPrettyRune(diffs []diffmatchpatch.Diff) []*DiffRune {
	result := []*DiffRune{}

	var offset int32 = 0
	for _, diff := range diffs {
		text := diff.Text
		runeOffset := []rune(text)

		switch diff.Type {
		case diffmatchpatch.DiffInsert:
			// offset是新文本的
			result = append(result, &DiffRune{
				Type:   diff.Type,
				Word:   text,
				Offset: offset,
			})
			offset += int32(len(runeOffset))
		case diffmatchpatch.DiffDelete:
			// offset是旧文本的
			result = append(result, &DiffRune{
				Type:   diff.Type,
				Word:   text,
				Offset: offset,
			})
		case diffmatchpatch.DiffEqual:
			offset += int32(len(runeOffset))
		}
	}

	return result
}

func DiffHtml(content string, newContent string, isDel bool) string {
	dmp := diffmatchpatch.New()
	diffs := dmp.DiffMain(content, newContent, false)
	return DiffPrettyHtml(diffs, isDel)
}
func DiffPrettyHtml(diffs []diffmatchpatch.Diff, isDel bool) string {
	var buff bytes.Buffer
	for _, diff := range diffs {
		text := strings.Replace(html.EscapeString(diff.Text), "\n", "&para;<br>", -1)
		switch diff.Type {
		case diffmatchpatch.DiffInsert:
			_, _ = buff.WriteString("<ins style=\"color:#19b955;\">")
			_, _ = buff.WriteString(text)
			_, _ = buff.WriteString("</ins>")
		case diffmatchpatch.DiffDelete:
			if isDel {
				_, _ = buff.WriteString("<del style=\"color:#f3cccc;\">")
				_, _ = buff.WriteString(text)
				_, _ = buff.WriteString("</del>")
			}
		case diffmatchpatch.DiffEqual:
			_, _ = buff.WriteString(text)
		}
	}
	return buff.String()
}
